#include <lobi/fm.h>
#include <assert.h>
#include <string.h>

int fm_open_file(char* filename,struct fm_t* fm)
{
	assert(fm);

	fm->type = FM_TYPE_FILE;

	/* always opens in binary mode */
	fm->fp =  fopen(filename,"rb");
	if (!fm->fp) return 1;
	return 0;
}

int fm_open_mem(char* data, unsigned len,struct fm_t* fm)
{
	assert(fm);
	assert(data);

	fm->type = FM_TYPE_MEM;
	fm->data = data;
	fm->len = len;
	fm->curp = data;
	return 0;
}

int fm_close(struct fm_t* fm)
{
	switch (fm->type) 
	{
		case FM_TYPE_FILE:
			return fclose(fm->fp);

		case FM_TYPE_MEM:
			return 0;

		default:
			return 1;
	}
}


size_t fm_read(void* ptr, size_t size, size_t nmemb,struct fm_t* fm)
{
	switch (fm->type) 
	{
		case FM_TYPE_FILE:
			return fread(ptr,size,nmemb,fm->fp);

		case FM_TYPE_MEM:
			{
				unsigned len = (unsigned)(size * nmemb);
				if (len) {
					if ((fm->curp - fm->data) + len <= fm->len) {
						memmove(ptr, fm->curp, len);
						fm->curp += len;
						return nmemb;
					}
				}
			}
			return 0;

		default:
			return 1;
	}
}
void fm_rewind(struct fm_t* fm)
{
	switch (fm->type) 
	{
		case FM_TYPE_FILE:
			rewind(fm->fp);
			return;

		case FM_TYPE_MEM:
			fm->curp = fm->data;
			return;
	}
}
int fm_seek(struct fm_t* fm, long offset, int whence)
{
	switch (fm->type) 
	{
		case FM_TYPE_FILE:
			return fseek(fm->fp,offset,whence);

		case FM_TYPE_MEM:
			switch (whence)
			{
				case SEEK_SET:
					fm->curp = fm->data + (int)offset;
					break;
				case SEEK_CUR:
					fm->curp += (int)offset;
					break;
				case SEEK_END:
					fm->curp = fm->data + fm->len - (int)offset;
					break;
			}
			return 0;
	}
	/* invalid fm-type */
	return 1;
}
